import { obj, effect } from "./index.js";

Mock.mock("/api/mock", "get", function (options) {
  return {
    code: 0,
    data: "mock data",
    message: "success",
    success: true,
    timestemp: Date.now(),
  };
});

/**
 * @description watch 侦听器
 * @param {*} source 监听的数据
 * @param {*} callback 数据变化执行的回调函数
 */
function watch(source, callback, options) {
  let getter;
  if (typeof source === "function") {
    getter = source;
  } else {
    getter = () => traverse(source);
  }
  let oldValue;
  let newValue;

  const effectFn = effect(() => getter(), {
    lazy: true,
    scheduler() {
      if (options?.flush === "post") {
        /* 在组件执行完之后执行 */
        console.log("在组件执行完之后执行");
        const p = Promise.resolve();
        p.then(watchEffectFn);
      } else if (options?.flush === "pre") {
        /* 在组件执行前执行 */
        console.log("在组件更新之前执行");
      } else {
        /* 直接执行 */
        watchEffectFn();
      }
    },
  });

  /* 存储过期回调 */
  let expireCallback;

  /* watch 过期执行的函数 */
  function onInvalidate(expireFn) {
    expireCallback = expireFn;
  }

  /**
   * @description scheduler 的调度函数
   * @param {*} fn 副作用函数
   */
  function watchEffectFn(fn) {
    console.log("fn", fn);
    console.log("effectFn", effectFn);
    console.log(fn === effectFn); // true

    newValue = effectFn();

    /* 在执行 watch 副作用函数之前执行过期回调函数  */
    if (expireCallback) {
      expireCallback();
    }

    callback(newValue, oldValue, onInvalidate);

    oldValue = newValue;
  }

  /* 如果用户 immeadite 为 true，watch的回调函数立即执行 */
  if (options?.immediate) {
    watchEffectFn();
  } else {
    oldValue = effectFn();
  }
}

watch(
  () => obj.foo,
  async (newValue, oldValue, onInvalidate) => {
    /* watch函数过期标识，默认为false表示未过期 */
    let expired = false;

    /* 在下一次watch的副作用函数执行之前的过期回调函数中将expired设置为true，表示watch已过期 */
    onInvalidate(() => {
      expired = true;
    });

    console.log(newValue, oldValue);
    const { data } = await axios.get("/api/mock");

    if (!expired) {
      console.log(data);
    }
  }
);

obj.foo++;

setTimeout(() => {
  obj.foo++;
}, 1500);
/**
 * @description 递归读取传进来的对象的属性
 * @param {*} value 对象
 * @param {*} seen 存储已读取的对象
 * @returns value
 */
function traverse(value, seen = new Set()) {
  if (typeof value !== "object" || value === null || seen.has(value)) return;

  seen.add(value);

  for (let key in value) {
    traverse(value[key], seen);
  }

  return value;
}
